Oracle creating a dynamic plsql command - oracle

I'm trying to put together a procedure, which counts the rows for each PARTITION in a table but I'm getting a syntax error:
Errors: PROCEDURE COUNT_PARTITION
Line/Col: 14/31 PLS-00103: Encountered the symbol "(" when expecting one of the following:
I know this isn't the most efficient way and I can use the num_rows column along with gathering statistics to achieve the same results.
Below is my test CASE. I know the problem is with the construction of the 'cmd' call but I can't seem to get it to work. Any help would be greatly appreciated.
CREATE OR REPLACE PROCEDURE cmd(p_cmd varchar2)
authid current_user
is
BEGIN
dbms_output.put_line(p_cmd);
execute immediate p_cmd;
END;
/
CREATE OR REPLACE PROCEDURE
count_partition(
p_tab varchar2
) authid current_user
is
v_cnt integer;
BEGIN
for cur_rec in (select table_name, partition_name,
partition_position
FROM user_tab_partitions where table_name = p_tab order by partition_position) loop
cmd ('select count(*) /*+ parallel(a,8) */
from ' ||p_tab|| 'PARTITION ' ('||cur_rec.partition_name||')' INTO v_cnt);
DBMS_OUTPUT.PUT_LINE(cur_rec.table_name || ' ' || cur_rec.partition_name || ' ' || v_cnt || ' rows');
end loop;
END;

Created a procedure and wrapper
CREATE OR REPLACE PROCEDURE
count_partition(
p_tab varchar2
) authid current_user
is
sql_stmt varchar2(1024);
row_count number;
cursor get_tab is
select table_name,
partition_name
from user_tab_partitions
where table_name=p_tab;
BEGIN
for get_tab_rec in get_tab loop
BEGIN
sql_stmt := 'select count(*) /*+ parallel(a,4) */ from ' ||get_tab_rec.table_name||' partition ( '||get_tab_rec.partition_name||' )';
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt INTO row_count;
dbms_output.put_line('Table '||rpad(get_tab_rec.table_name
||'('||get_tab_rec.partition_name||')',50)
||' '||TO_CHAR(row_count)||' rows.');
exception when others then
dbms_output.put_line
('Error counting rows for table '||get_tab_rec.table_name);
END;
END LOOP;
END;
/
BEGIN
FOR cur_r IN(
SELECT TABLE_NAME FROM USER_PART_TABLES
)
LOOP
--DBMS_OUTPUT.put_line('Table '|| cur_r.table_name);
count_partition (cur_r.table_name);
END LOOP;
END;
~~

Related

Why do I get "ORA-00933: SQL command not properly ended" error ( execute immediate )?

I created a function and it uses a dynamic sql:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
select sign(count(1))
into :l_res
from '|| table_name ||'
where '|| code_name ||' = :code_value
';
execute immediate l_query
using in code_value, out l_res;
return l_res;
end;
But when I try to use it I get an exception "ORA-00933: SQL command not properly ended"
What is wrong with this code?
You can use EXECUTE IMMEDIATE ... INTO ... USING ... to get the return value and DBMS_ASSERT to raise errors in the case of SQL injection attempts:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := 'select sign(count(1))'
|| ' from ' || DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)
|| ' where ' || DBMS_ASSERT.SIMPLE_SQL_NAME(code_name)
|| ' = :code_value';
execute immediate l_query INTO l_res USING code_value;
return l_res;
end;
/
Which, for the sample data:
CREATE TABLE abc (a, b, c) AS
SELECT 1, 42, 3.14159 FROM DUAL;
Then:
SELECT CHECK_REF_VALUE('abc', 42, 'b') AS chk FROM DUAL;
Outputs:
CHK
1
And:
SELECT CHECK_REF_VALUE('abc', 42, '1 = 1 OR b') AS chk FROM DUAL;
Raises the exception:
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 160
ORA-06512: at "FIDDLE_UVOFONEFDEHGDQJELQJL.CHECK_REF_VALUE", line 10
As for your question:
What is wrong with this code?
Using SELECT ... INTO is only valid in an SQL statement in a PL/SQL block and when you run the statement via EXECUTE IMMEDIATE it is executed in the SQL scope and not a PL/SQL scope.
You can fix it by wrapping your dynamic code in a BEGIN .. END PL/SQL anonymous block (and reversing the order of the bind parameters in the USING clause):
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
BEGIN
select sign(count(1))
into :l_res
from '|| DBMS_ASSERT.SIMPLE_SQL_NAME(table_name) ||'
where '|| DBMS_ASSERT.SIMPLE_SQL_NAME(code_name) ||' = :code_value;
END;
';
execute immediate l_query
using out l_res, in code_value;
return l_res;
end;
/
(However, that is a bit more of a complicated solution that just using EXECUTE IMMEDIATE ... INTO ... USING ....)
db<>fiddle here

oracle apex query sql injection

i have check my sql query which is vulnerable to sql injection.
V_NAME is detected sql injection.
how can I securing my query ?
this is my query :
FUNCTION "GET_SEQUENCE" (P_BID VARCHAR2, P_PSC VARCHAR2) RETURN NUMBER AS
TYPE T_HASIL IS TABLE OF NUMBER;
V_HASIL T_HASIL;
V_NAME VARCHAR2(30);
V_SQL LONG;
BEGIN
SELECT KEYSEQ INTO V_NAME
FROM MST_SEQUENCE_DETAIL Tbl
WHERE BRANCHCODE=P_BID AND KEYCODE=P_PSC
AND YEAR = TO_CHAR(SYSDATE,'RRRR');
V_SQL := 'SELECT ' || V_NAME || '.NEXTVAL FROM DUAL';
EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO V_HASIL;
RETURN V_HASIL(1);
END;
thank u.
Your function is vulnerable to SQL injection.
Consider if someone performs this insert:
INSERT INTO MST_SEQUENCE_DETAIL (
BRANCHCODE,
KEYCODE,
YEAR,
KEYSEQ
) VALUES (
1,
1,
TO_CHAR( SYSDATE, 'RRRR' ),
'(SELECT psswd FROM usr),keyseq'
);
Then calling your function:
GET_SEQUENCE( 1, 1 );
Will set the query to:
V_SQL := 'SELECT (SELECT psswd FROM usr),keyseq.NEXTVAL FROM DUAL';
The next statement:
EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO V_HASIL;
Will fail but how it fails can tell you whether:
There is a usr table; if is there is not then you will get the exception (SQLFiddle):
ORA-00942: table or view does not exist ORA-06512: at "USER_4_C4D95A.GET_SEQUENCE", line 18
It has a column called psswd; if there is not then you will get the exception (SQLFiddle)
ORA-00904: "PSSWD": invalid identifier ORA-06512: at "USER_4_9B4C87.GET_SEQUENCE", line 18
Performing this repeatedly, you can start to map the structure of the database and look for other vulnerabilities that may allow greater exploits.
V_SQL := 'SELECT ' || V_NAME || '.NEXTVAL FROM DUAL';
EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO V_HASIL;
The issue here is that V_NAME could in theory be any SQL code and thus an injection vulnerability. The way you protect this is to use dbms_assert.simple_sql_name, since you expect this variable to be a simple identifier:
V_SQL := 'SELECT ' || sys.dbms_assert.simple_sql_name(V_NAME) || '.NEXTVAL FROM DUAL';
EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO V_HASIL;

Getting inconsistent data type error while fetching bulk collect from ref cursor

I am getting inconsistent data type error while fetching bulk collect from ref cursor
Here's my code
create or replace function test_func(c in int) return number as
v_stmt varchar2(4000);
v_insert varchar2(4000);
k_col_name varchar2(50);
v_gs_tab_name varchar2(100);
min_k_val varchar2(50);
max_k_val varchar2(50);
mid_k_val varchar2(50);
nxt_mid_k_val varchar2(50);
type l_cursor_type is ref cursor;
l_cursor l_cursor_type;
type t_source is table of rec_source;
m_source t_source:=t_source();
dat_typ varchar2(50);
begin
select DISTINCT data_type INTO dat_typ from all_tab_cols where column_name
in (SELECT cols.column_name FROM all_constraints cons, all_cons_columns
cols
WHERE cons.constraint_type='P' and cols.table_name=+v_tab_name AND
CONS.OWNER='SOURCE'
AND cons.constraint_name = cols.constraint_name AND cons.owner =
cols.owner);
dml_str:='create or replace type dwbi_land.rec_source as object (id '||dat_typ||')';
dbms_output.put_line(dml_str);
execute immediate dml_str;
dml_str:='create or replace type dwbi_land.t_source as table of rec_source';
dbms_output.put_line(dml_str);
execute immediate dml_str;
begin
select 'select * from '||v_gs_tab_name
||' where '||k_col_name||' between ('||min_k_val||') and
('||mid_k_val||')'
into v_stmt
from dual;
dbms_output.put_line(v_stmt);
execute immediate v_stmt bulk collect into m_source;
v_insert:='insert into '||v_tab_name||' values '||v_stmt||'';
dbms_output.put_line(v_insert);
open l_cursor for v_stmt;
loop
fetch l_cursor bulk collect into m_source;
exit when l_cursor%NOTFOUND;
m_source.extend;
forall i in 1..m_source.count
execute immediate v_insert using m_source(i).id;
commit;
exit when m_source.count=0;
end loop;
select 'select * from '||v_gs_tab_name
||' where '||k_col_name||' between ('||nxt_mid_k_val||') and ('||max_k_val||')'
into v_stmt
from dual;
dbms_output.put_line(v_stmt);
v_insert:='insert into '||v_tab_name||' values '||v_stmt||'';
dbms_output.put_line(v_insert);
open l_cursor for v_stmt using 1;
loop
fetch l_cursor bulk collect into m_source;
forall i in 1..m_source.count
execute immediate v_insert using m_source(i).id;
commit;
exit when m_source.count=0;
end loop;
end;
In fetch l_cursor bulk collect into m_source part am getting
Ora-00932:Inconsistent datatypes: expected - got -
Any suggestions are welcome
Previously i wasn't able to post the whole code, now i've posted the whole code
You are missing the declaration of variables but it appears you could simplify it to:
begin
v_stmt := 'insert into ' || v_tab_name
|| ' select id from '||v_gs_tab_name
|| ' where '||k_col_name||' between :1 and :2;
dbms_output.put_line(v_stmt);
execute immediate v_stmt using min_k_val, mid_k_val;
execute immediate v_stmt using nxt_mid_k_val, max_k_val;
END;
can you plz expand ur coding...
wats the datatype for v_stmt ?
Sample code :
begin
v_stmt:= 'select * from ''||v_gs_tab_name||'' where ''||k_col_name||'' between (''||nxt_mid_k_val||'') and (''||max_k_val||'')'' into v_stmt from dual'
Dbms_output.put_line(v_stmt);
V_insert:='insert into '||v_tab_name||' values '||v_stmt||'';
dbms_output.put_line(v_insert);
open l_cursor for v_stmt using 1; loop fetch l_cursor bulk collect into m_source;
forall i in 1..m_source.count
execute immediate v_insert using m_source(i).id;
commit;
exit when m_source.count=0;
end loop;
end;

Error(20,28): PL/SQL: ORA-00942: table or view does not exist

I have ORACLE PROCEDURE, where I need to use dynamic table name in select query based on parameter "a_ID" passed.
But I am getting error in creating procedure.
Error(20,28): PL/SQL: ORA-00942: table or view does not exist
Can anybody help resolving this?
Below is the proc....
CREATE OR REPLACE
PROCEDURE IsTrue
(
AppID IN VARCHAR2,
a_ID IN VARCHAR2,
l_ID VARCHAR2,
return_value out CHAR
)
AS
v_sql VARCHAR(3000);
v_tablename VARCHAR(30);
BEGIN
SELECT decode(a_ID,'LS','LSAPP','MR','MRAPP','RV','RVAPP','APP') INTO v_tablename FROM dual;
SELECT CASE
WHEN EXISTS (SELECT 1
FROM v_tablename
WHERE APPID = AppID
AND LID <> l_ID)
THEN 'Y'
ELSE 'N'
END AS RECORD_EXISTS
FROM DUAL;
EXECUTE IMMEDIATE v_sql INTO return_value;
END;
CREATE OR REPLACE PROCEDURE IsTrue (argAppID IN VARCHAR2, a_ID IN VARCHAR2, l_ID VARCHAR2, return_value out CHAR )
AS
v_sql VARCHAR(3000);
v_tablename VARCHAR(30);
BEGIN
SELECT decode(a_ID,'LS','LSAPP','MR','MRAPP','RV','RVAPP','APP') INTO v_tablename FROM dual;
v_sql := 'SELECT CASE WHEN EXISTS (SELECT 1 FROM ' || v_tablename || ' WHERE APPID = '''|| argAppID ||''' AND LID <> '''|| l_ID || ''') THEN ''Y'' ELSE ''N'' END AS RECORD_EXISTS FROM DUAL';
EXECUTE IMMEDIATE v_sql INTO return_value;
END;
In your procedure the second line created as dynamic sql and stored in v_sql.
This may help your procedure to execute as you need.

Why the Oracle procedure not compiling?

CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
sql_stmt VARCHAR2(2000);
BEGIN
FOR sql_stmt IN (select 'update '|| table_name ||
' set version = 0'
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE sql_stmt;
END LOOP;
COMMIT;
END;
Why the about procedure is not compiling? Its giving error saying
Error(8,26): PLS-00382: expression is of wrong type
How to resolve this?
Two things:
You need to remember that when iterating a cursor it returns a ROW, not a VALUE, and
The SELECT in the cursor needs to give a name to the value in the row being generated by the cursor.
Try:
CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
BEGIN
FOR aRow IN (select 'update '|| table_name ||
' set version = 0' AS SQL_STMT
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE aRow.SQL_STMT;
END LOOP;
COMMIT;
END;
Share and enjoy.
CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
BEGIN
FOR sql_stmt IN (
select 'update '|| table_name || ' set version = 0' as x
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE sql_stmt.x;
END LOOP;
COMMIT;
END;

Resources