Error “ORA-0091:Invalid Character” when executing a PL-SQL statement in Oracle [closed]

dba.stackexchange https://dba.stackexchange.com/questions/267444

سؤال

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.

هل كانت مفيدة؟

المحلول

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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى dba.stackexchange
scroll top