Error “ORA-0091:Invalid Character” when executing a PL-SQL statement in Oracle [closed]
-
02-03-2021 - |
Question
I'm new to oracle pl-sql and I'm facing error ORA-0091:Invalid character
when executing this query below. But when I comment the Execute Immediate
statement and use dbms_output.put_line
the result of the variable qry
is fine and there is no problem with the query inside qry
variable.
declare
qry varchar2(5000);
effdate date := to_date('5/19/2020', 'mm/dd/yyyy');
user_id varchar2(1000) := 'pantea';
search_number varchar2(50) := '8080';
deposit_filter_query varchar2(5000) := ' deposit_group_code = -1
and deposit_group_desc = -1
and currency_desc = ''-1''
group by customer_num
having sum(dep_avg_balance) > 1;';
begin
qry := 'insert into dep_filter_cust
select distinct customer_num ,''' || search_number || '''
from vmi_customer_deposit_detail v
inner join branchlf_user b
on b.userid = ''' || user_id || '''
and dep_branch_cod = v.dep_branch_cod
where effective_date = to_date(''' ||
to_char(effdate, 'mm/dd/yyyy') || ''','' mm/dd/yyyy'') and ' ||
deposit_filter_query;
--dbms_output.put_line(qry);
execute immediate to_char(qry);
commit;
end;
I was wondering if you could help me finding the problem here . Thanks in advance.
Solution
As mentioned in the comments, your original problem is the unnecessary semicolon ( ;
).
Code Review
By building the query the way you did, you are introducing SQL Injection Vulnerability. (obligatory XKCD reference). To prevent SQL Injections, use BIND variables.
Most RDBMS allow you to use ?
as a place holder. Oracle supports this and Named place holders (eg :SEARCH_NUM
). By using a Bind variable, DATE data type will be bound as DATEs, etc. This removes the necessity for TO_CHAR
/TO_NUMBER
.
Since your code is really dynamic, I highly advise using the DBMS_SQL
package to parse your SQL statement and Bind the variables. This is especially true if the usage of Binds will be dynamic also. (eg use two variables one time, use 3 another, etc.)
Documentation for DBMS_SQL
can be seen here.
Example code:
declare
qry varchar2(5000);
effdate date := to_date('5/19/2020', 'mm/dd/yyyy');
user_id varchar2(1000) := 'pantea';
search_number varchar2(50) := '8080';
deposit_filter_query varchar2(5000) := ' deposit_group_code = -1
and deposit_group_desc = -1
and currency_desc = ''-1''
group by customer_num
having sum(dep_avg_balance) > 1'; -- removed semi-colon
initial_dml varchar2(5000) := 'insert into dep_filter_cust (col1, col2)' || chr(10) -- alway define the columns in INSERT
|| 'select distinct customer_num , :SEARCH_NUM
from vmi_customer_deposit_detail v
inner join branchlf_user b
on b.userid = :USER_ID
and dep_branch_cod = v.dep_branch_cod
where effective_date = :EFFDATE' || CHR(10); -- chr(10) === \n; NEWLINE character for visualisation
actual_sql varchar2(32767);
c pls_integer;
ret PLS_INTEGER;
begin
-- build your SQL (I do this in a separate function)
actual_sql := initial_dml || ' and ' || deposit_filter_query;
-- debug results
dbms_output.put_line( actual_sql );
-- get your cursor
c := dbms_sql.open_cursor();
-- parse the SQL
dbms_sql.parse( c, actual_sql, DBMS_SQL.NATIVE );
-- bind variables (this can be dynamic if needed)
-- Oracle can use NAMED Binds
dbms_sql.bind_variable( c, ':SEARCH_NUM', search_number );
dbms_sql.bind_variable( c, ':USER_ID', user_id );
dbms_sql.bind_variable( c, ':EFFDATE', effdate );
-- execute
ret := dbms_sql.execute( c );
-- close the cursor
dbms_sql.close_cursor( c );
end;
/
If you don't do that, at the very least, use the USING
clause of EXECUTE IMMEDIATE
.
eg
execute immediate actual_sql using search_number, user_id, effdate;