patternMinor
Building Dynamic Oracle Where Clause
Viewed 0 times
wherebuildingdynamicoracleclause
Problem
I'm working on application that uses dynamic query to do a select statement based on user input, after discussing security with DBAs they want me to convert my dynamic select statement into Stored Procedure.
I have built dynamic sql using MSSQL but I can not figure out how to convert it to Oracle SQL.
*please note that I want to prevent SQL injection I do not want to just add string together
**i have built a separate class for creating this dynamic query for my application in .net I have almost 1000 lines of code to handle everything and prevent sql injection, but DBAs have told me that they want stored procedures so they can control input and output.
I have built dynamic sql using MSSQL but I can not figure out how to convert it to Oracle SQL.
CREATE PROCEDURE GetCustomer
@FirstN nvarchar(20) = NULL,
@LastN nvarchar(20) = NULL,
@CUserName nvarchar(10) = NULL,
@CID nvarchar(15) = NULL as
DECLARE @sql nvarchar(4000),
SELECT @sql = 'C_FirstName, C_LastName, C_UserName, C_UserID ' +
'FROM CUSTOMER ' +
'WHERE 1=1 ' +
IF @FirstN IS NOT NULL
SELECT @sql = @sql + ' AND C_FirstName like @FirstN '
IF @LastN IS NOT NULL
SELECT @sql = @sql + ' AND C_LastName like @LastN '
IF @CUserName IS NOT NULL
SELECT @sql = @sql + ' AND C_UserName like @CUserName '
IF @CID IS NOT NULL
SELECT @sql = @sql + ' AND C_UserID like @CID '
EXEC sp_executesql @sql, N'@C_FirstName nvarchar(20), @C_LastName nvarchar(20), @CUserName nvarchar(10), @CID nvarchar(15)',
@FirstN, @LastN, @CUserName, @CID*please note that I want to prevent SQL injection I do not want to just add string together
**i have built a separate class for creating this dynamic query for my application in .net I have almost 1000 lines of code to handle everything and prevent sql injection, but DBAs have told me that they want stored procedures so they can control input and output.
Solution
This might give you an idea:
Later, in SQL*Plus:
Edit: The comment is right, and the procedure is subject to SQL injection. So, in order to prevent that, you could go with bind variables such as in this modified procedure:
Note, that now, whatever the input, the select statement becomes something like
create table Customer (
c_firstname varchar2(50),
c_lastname varchar2(50),
c_userid varchar2(50)
);
insert into Customer values ('Micky' , 'Mouse', 'mm');
insert into Customer values ('Donald', 'Duck' , 'dd');
insert into Customer values ('Peter' , 'Pan' , 'pp');
create or replace function GetCustomer(
FirstN varchar2 := null,
LastN varchar2 := null,
CID varchar2 := null
) return sys_refcursor
as
stmt varchar2(4000);
ret sys_refcursor;
begin
stmt := 'select * from Customer where 1=1';
if FirstN is not null then
stmt := stmt || ' and c_firstname like ''%' || FirstN || '%''';
end if;
if LastN is not null then
stmt := stmt || ' and c_lastname like ''%' || LastN || '%''';
end if;
if CID is not null then
stmt := stmt || ' and c_userid like ''%' || CID || '%''';
end if;
dbms_output.put_line(stmt);
open ret for stmt;
return ret;
end;
/Later, in SQL*Plus:
set serveroutput on size 100000 format wrapped
declare
c sys_refcursor;
fn Customer.c_firstname%type;
ln Customer.c_lastname %type;
id Customer.c_userid %type;
begin
c := GetCustomer(LastN => 'u');
fetch c into fn, ln, id;
while c%found loop
dbms_output.put_line('First Name: ' || fn);
dbms_output.put_line('Last Name: ' || ln);
dbms_output.put_line('user id: ' || id);
fetch c into fn, ln, id;
end loop;
close c;
end;
/Edit: The comment is right, and the procedure is subject to SQL injection. So, in order to prevent that, you could go with bind variables such as in this modified procedure:
create or replace function GetCustomer(
FirstN varchar2 := null,
LastN varchar2 := null,
CID varchar2 := null
) return sys_refcursor
as
stmt varchar2(4000);
ret sys_refcursor;
type parameter_t is table of varchar2(50);
parameters parameter_t := parameter_t();
begin
stmt := 'select * from Customer where 1=1';
if FirstN is not null then
parameters.extend;
parameters(parameters.count) := '%' || FirstN || '%';
stmt := stmt || ' and c_firstname like :' || parameters.count;
end if;
if LastN is not null then
parameters.extend;
parameters(parameters.count) := '%' || LastN || '%';
stmt := stmt || ' and c_lastname like :' || parameters.count;
end if;
if CID is not null then
parameters.extend;
parameters(parameters.count) := '%' || CID || '%';
stmt := stmt || ' and c_userid like :' || parameters.count;
end if;
if parameters.count = 0 then
open ret for stmt;
elsif parameters.count = 1 then
open ret for stmt using parameters(1);
elsif parameters.count = 2 then
open ret for stmt using parameters(1), parameters(2);
elsif parameters.count = 3 then
open ret for stmt using parameters(1), parameters(2), parameters(3);
else raise_application_error(-20800, 'Too many parameters');
end if;
return ret;
end;
/Note, that now, whatever the input, the select statement becomes something like
select ... from ... where 1=1 and col1 like :1 and col2 :2 ... which is obviously much safer.Code Snippets
create table Customer (
c_firstname varchar2(50),
c_lastname varchar2(50),
c_userid varchar2(50)
);
insert into Customer values ('Micky' , 'Mouse', 'mm');
insert into Customer values ('Donald', 'Duck' , 'dd');
insert into Customer values ('Peter' , 'Pan' , 'pp');
create or replace function GetCustomer(
FirstN varchar2 := null,
LastN varchar2 := null,
CID varchar2 := null
) return sys_refcursor
as
stmt varchar2(4000);
ret sys_refcursor;
begin
stmt := 'select * from Customer where 1=1';
if FirstN is not null then
stmt := stmt || ' and c_firstname like ''%' || FirstN || '%''';
end if;
if LastN is not null then
stmt := stmt || ' and c_lastname like ''%' || LastN || '%''';
end if;
if CID is not null then
stmt := stmt || ' and c_userid like ''%' || CID || '%''';
end if;
dbms_output.put_line(stmt);
open ret for stmt;
return ret;
end;
/set serveroutput on size 100000 format wrapped
declare
c sys_refcursor;
fn Customer.c_firstname%type;
ln Customer.c_lastname %type;
id Customer.c_userid %type;
begin
c := GetCustomer(LastN => 'u');
fetch c into fn, ln, id;
while c%found loop
dbms_output.put_line('First Name: ' || fn);
dbms_output.put_line('Last Name: ' || ln);
dbms_output.put_line('user id: ' || id);
fetch c into fn, ln, id;
end loop;
close c;
end;
/create or replace function GetCustomer(
FirstN varchar2 := null,
LastN varchar2 := null,
CID varchar2 := null
) return sys_refcursor
as
stmt varchar2(4000);
ret sys_refcursor;
type parameter_t is table of varchar2(50);
parameters parameter_t := parameter_t();
begin
stmt := 'select * from Customer where 1=1';
if FirstN is not null then
parameters.extend;
parameters(parameters.count) := '%' || FirstN || '%';
stmt := stmt || ' and c_firstname like :' || parameters.count;
end if;
if LastN is not null then
parameters.extend;
parameters(parameters.count) := '%' || LastN || '%';
stmt := stmt || ' and c_lastname like :' || parameters.count;
end if;
if CID is not null then
parameters.extend;
parameters(parameters.count) := '%' || CID || '%';
stmt := stmt || ' and c_userid like :' || parameters.count;
end if;
if parameters.count = 0 then
open ret for stmt;
elsif parameters.count = 1 then
open ret for stmt using parameters(1);
elsif parameters.count = 2 then
open ret for stmt using parameters(1), parameters(2);
elsif parameters.count = 3 then
open ret for stmt using parameters(1), parameters(2), parameters(3);
else raise_application_error(-20800, 'Too many parameters');
end if;
return ret;
end;
/Context
StackExchange Database Administrators Q#667, answer score: 6
Revisions (0)
No revisions yet.