Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning dataset with PL/SQL and a variable table name

I'm trying to write a PL/SQL function to store a select statement with a variable table name (a bit weird i know but it is actually a good design decision). The following code does not work...but I'm not sure how to both take a variable table name (building the query) and return a dataset. Anyone have any experience in this? TIA.

CREATE OR REPLACE FUNCTION fn_netstat_all (casename in varchar2) 
RETURN  resultset_subtype
IS
  dataset resultset_subtype;
  v_sql varchar2(25000);
  v_tablename varchar2(50);

begin

  v_sql := 'SELECT * FROM ' || casename || '_netstat;'; 

  OPEN dataset FOR
    execute immediate v_sql;
  return dataset;

end;
like image 271
coergo Avatar asked Jun 08 '26 22:06

coergo


2 Answers

If your resultset_subtype is a ref_cursor (or just replace resultset_subtype with a ref_cursor) you could:

CREATE OR REPLACE
FUNCTION fn_netstat_all (
   casename IN VARCHAR2
)
RETURN resultset_subtype
IS
   dataset resultset_subtype;
BEGIN
   OPEN dataset
   FOR 'SELECT * FROM ' || casename || '_netstat';

   RETURN dataset;
END fn_netstat_all;

FWIW, you might want to look into the DBMS_ASSERT package to wrap the casename variable to help protect against SQL Injection attacks in your dynamic SQL.

Hope it helps...

like image 134
Ollie Avatar answered Jun 10 '26 19:06

Ollie


Below it is assumed all tables are similar. It's also possible to select a subset of colums that are similar in every table without using DBMS_SQL. I have also paid some attention to SQL injection mentioned by Ollie.

create table so9at (
  id number(1),
  data varchar2(5)
);

insert into so9at values (1, 'A-AAA');
insert into so9at values (2, 'A-BBB');
insert into so9at values (3, 'A-CCC');

create table so9bt (
  id number(1),
  data varchar2(5)
);

insert into so9bt values (5, 'B-AAA');
insert into so9bt values (6, 'B-BBB');
insert into so9bt values (7, 'B-CCC');

create table secret_identities (
  cover_name varchar2(20),
  real_name varchar2(20)
);

insert into secret_identities values ('Batman', 'Bruce Wayne');
insert into secret_identities values ('Superman', 'Clark Kent');

/* This is a semi-secure version immune to certain kind of SQL injections. Note
that it can be still used to find information about any table that ends with
't'. */
create or replace function cursor_of (p_table_id in varchar2)
return sys_refcursor as
  v_cur sys_refcursor;
  v_stmt constant varchar2(32767) := 'select * from ' || dbms_assert.qualified_sql_name(p_table_id || 't');
begin
  open v_cur for v_stmt;
  return v_cur;
end;
/
show errors

/* This is an unsecure version vulnerable to SQL injection. */
create or replace function vulnerable_cursor_of (p_table_id in varchar2)
return sys_refcursor as
  v_cur sys_refcursor;
  v_stmt constant varchar2(32767) := 'select * from ' || p_table_id || 't';
begin
  open v_cur for v_stmt;
  return v_cur;
end;
/
show errors

create or replace procedure print_values_of (p_cur in sys_refcursor) as
  type rec_t is record (
    id number,
    data varchar2(32767)
  );
  v_rec rec_t;
begin
  fetch p_cur into v_rec;

  while p_cur%found loop
    dbms_output.put_line('id = ' || v_rec.id || ' data = ' || v_rec.data);
    fetch p_cur into v_rec;
  end loop;

end;
/
show errors

declare
  v_cur sys_refcursor;
begin
  v_cur := cursor_of('so9a');
  print_values_of(v_cur);
  close v_cur;

  v_cur := cursor_of('so9b');
  print_values_of(v_cur);
  close v_cur;

  /* SQL injection vulnerability */
  v_cur := vulnerable_cursor_of('secret_identities --');
  dbms_output.put_line('Now we have a cursor that reveals all secret identities. Just see DBMS_SQL.DESCRIBE_COLUMNS ...');
  close v_cur;

  /* SQL injection made (mostly) harmless - will throw ORA-44004: invalid qualified SQL name */
  v_cur := cursor_of('secret_identities --');
  close v_cur;
end;
/
like image 24
user272735 Avatar answered Jun 10 '26 20:06

user272735