I have this set of code, it didn't return error when I compiled it.
create or replace PROCEDURE PARAM_CHECK
(
PARAM_CODE IN VARCHAR2,
TABLE_NAME IN VARCHAR2,
ISEXIST OUT INTEGER
)
IS
SQLSTMT VARCHAR2(200);
V_COUNT NUMBER;
BEGIN
SQLSTMT := 'SELECT COUNT(*) INTO '|| V_COUNT ||' FROM '|| TABLE_NAME ||' WHERE ID = '''||PARAM_CODE||''' AND ROWNUM = 1';
EXECUTE IMMEDIATE SQLSTMT;
COMMIT;
IF
V_COUNT = 0 THEN
ISEXIST := 0;
ELSE
ISEXIST := 1;
END IF;
END PARAM_CHECK;
But when after I try to run the procedure, it return this error:
Connecting to the database SAA.
ORA-00936: missing expression
ORA-06512: at "SAA.PARAM_CHECK", line 12
ORA-06512: at line 9
Process exited.
Disconnecting from the database SAA.
I have no idea why it return this error, plus when I compile everything is okay. I did search for the specific ORA error but didn't help much. Please help me. Thanks in advance.
Vcount is uninitialised at the time of the inclusion of its value into the sql statement so it produces a sytactically wrong blank at this position.
If it was feasible at all you'd have to write
SQLSTMT := 'SELECT COUNT(*) INTO V_COUNT FROM '|| TABLE_NAME ||' WHERE ID = '''||PARAM_CODE||''' AND ROWNUM = 1';
but this won't work either iirc since execute immediate opens a new scope.
You have to resort to
SQLSTMT := 'SELECT COUNT(*) FROM '|| TABLE_NAME ||' WHERE ID = '''||PARAM_CODE||''' AND ROWNUM = 1';
EXecute immediate sqlstmt into v_count;
Related
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;
~~
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
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;
See Second Edit. New coding gets different error.
I am getting the "encountered a loop when expecting an if" error. My goal is to return a list of tables that have been updated by a specific project_id. The project_id is stored in a column on each table labeled project_id.
I used a with statement to create 2 tables. One table (sb_table) is a single column of all the tables that I want to look at. There are many more tables, but I figured it would speed things up if I narrowed down the list first. The other table I am creating (project) returns a single value turning the prjt_name provided into the actual project number (don't ask me why, but this is how my company has it set up, user creates a prct_name and is never aware of the project number).
Then I am trying to loop through the tables to see if they have the project number in their project_id column. If they do not, I delete them from the sb_table.
Ultimately, I am going to want to get all of the updated rows from all of the updated tables, but I am currently stuck on getting a list of the updated tables.
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := "01213264B";
cursor my_cur is select sbt.table_name from sb_table sbt;
begin
with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))
--select sbt.table_name
--from sb_table sbt
for tableName in my_cur loop
query := 'select count(t.project_id) as "CNT" '||
'from sandbox.' || tableName || '#db2 t, project p '||
' where t.project_id = p.project_id ';
Execute immediate query
into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end loop;
end;
Edit 1: Per comments, moved the select statement to the declare and am looping through my_cur now. I still get the same error.
Edit 2: Updated coding based on suggestions. I now get a different error message.
NEW ERROR: ORA-06550: line 12, column 16:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 12, column 7:
Edit 3: I was missing an = in my sub query which produced the error "Missing expression at line 13."
Edit 4: Now I get some results then error out with the following message
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-04040: file ext_qsp_benefit.dat in DATA_DIR not found
ORA-02063: preceding 3 lines from ADHOC_POS15
ORA-06512: at line 13
Edit 5: Success! Apparently I cannot query certain tables. So I just took those tables out.
Final coding is:
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is (select table_name from all_tab_columns#db2 where column_name = 'PROJECT_ID' and owner = 'SANDBOX' and table_name in ('X') );
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
execute immediate
'select count(project_id) as "CNT" from sandbox.' || tableName || '#db2 '
|| ' where project_id = (select project_id from sandbox.sb_project#db2 where project_name = ''' || prjt_name || ''' ) '
into update_count;
if update_count > 0 then
dbms_output.put_line (tableName);
end if;
end loop;
close my_cur;
end;
This doesn't do exactly what I wanted. It sends the results to dbms_output. But It is a start! Thanks everyone for you help!
DECLARE
update_count integer := 0;
prjt_name varchar2(100) := 'tttt';
mysql varchar2(100);
tablename varchar2(100);
cursor my_cur is
select 'DUAL'
from dual
where 'PROJECT_ID' = 'PROJECT_ID' and 'SANDBOX' = 'SANDBOX';
begin
open my_cur;
LOOP
FETCH my_cur into tablename;
EXIT WHEN my_cur%NOTFOUND;
update_count := 0;
mysql := 'select count(*) ' || ' from '
|| tablename
|| ' where ''TTTT'' = upper('''
|| prjt_name
|| ''')' ;
Execute immediate mysql INTO update_count;
dbms_output.put_line (mysql);
dbms_output.put_line (update_count);
END LOOP;
CLOSE my_cur;
end;
I tried one like yours. This executes successfully
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is
select table_name
from all_tab_columns#adhoc_pos15
where column_name = 'PROJECT_ID' and owner = 'SANDBOX';
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
query := 'select count(t.project_id) as ''CNT'' from sandbox.'
|| tableName
|| '#adhoc_pos15 t'
|| ' where t.project_id = (select project_id from sandbox.sb_project#adhoc_pos15 where project_name = ''' || prjt_name || ''') ' ;
execute immediate query into update_count;
dbms_output.put_line (query);
dbms_output.put_line (update_count);
end loop;
close my_cur;
end;
i have tried your code with some execution, but before that you need to correct your with clause query. below code has executed except with clause so, make some change dependence on your requirements.
**code :**
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := '01213264B';
cursor my_cur is
select sbt.table_name from sb_table sbt;
begin
/* with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))*/
for tableName in my_cur
loop
query := 'select count(t.project_id) into '|| update_count || 'from sandbox.' || tableName || '#db2 t, project p '||' where t.project_id = p.project_id ';
Execute immediate query;
--into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end if;
end loop;
end;
If you run it in sql-developer please note, there are different buttons for running query (green triangle) and running script (green smaller triangle over a white document). Use 'run script' button
how can i do if my select dont come with result using SYS_REFCURSOR ?
what i have try so far is using NO_DATA_FOUND, but its not working, my STATUS keep returning me as = 1
code:
...
MYVARIABLE IN OUT SYS_REFCURSOR
...
OPEN MYVARIABLE FOR
SELECT NAME FROM TABLE WHERE COD = 1;
STATUS := 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
STATUS := 0;
any ideias ?
thanks!
the real sql is something like this:
Edit
V_SQL := 'SELECT SUM(T1.VLRLIQ) VALOR,T1.CODCLI,T1.NOMCLI
,(SELECT METCLI FROM WEB_CRM_CLIVEN T2
WHERE T2.CODCLI = T1.CODCLI AND T2.CODVEN = '|| P_CODVEN ||'
AND T2.MES = '|| V_MES ||' AND T2.ANO = '|| V_ANO ||') META
FROM SAPIENS.USU_VRESNFV T1,SAPIENS.E085CLI T2
WHERE T1.CODVEN = '|| P_CODVEN ||'
AND TO_CHAR(T1.DATEMI,''YYYY'') = '|| V_ANO ||'
AND TO_CHAR(T1.DATEMI,''MM'') = '|| V_MES ||'
AND T1.VENFAT = ''S''
'|| V_CGCCPF ||'
'|| V_NOMCLI ||'
AND T2.CODCLI = T1.CODCLI
AND T1.CODEMP = 1
GROUP BY T1.CODCLI,T1.NOMCLI
UNION
SELECT SUM(''0'') VALOR,CODCLI,NOMCLI,(SELECT METCLI FROM WEB_CRM_CLIVEN T3
WHERE T3.CODCLI = T2.CODCLI AND T3.CODVEN = '|| P_CODVEN ||'
AND T3.MES = '|| V_MES ||' AND T3.ANO = '|| V_ANO ||') META
FROM SAPIENS.E085CLI T2
WHERE
CODCLI IN (SELECT CODCLI FROM WEB_CRM_VEN_CARTEIRA
WHERE CODVEN = '|| P_CODVEN ||' AND MES = '|| V_MES ||' AND ANO = '|| V_ANO ||')
'|| V_CGCCPF ||'
'|| V_NOMCLI ||'
GROUP BY CODCLI,NOMCLI
ORDER BY VALOR DESC';
STATUS := 1;
OPEN RESULTADO FOR V_SQL;
In your code, you are just opening the cursor but not fetching from it. When you open a cursor, PL/SQL executes the query for that cursor. It also identifies the rows that meet the criteria in the WHERE clause and join conditions. The OPEN does not actually retrieve any of these rows; that action is performed by the FETCH statement. You would then use cursor attributes to check if the result set is empty; if it is, then the following cursor attributes would have these values: %FOUND = FALSE, %NOTFOUND = TRUE, and %ROWCOUNT = 0.
Here is an example:
SQL> DECLARE
2 l_cur SYS_REFCURSOR;
3 l_col VARCHAR2 (10);
4 BEGIN
5 OPEN l_cur FOR
6 SELECT 'Hi there' col
7 FROM DUAL
8 WHERE 1 = 0;
9
10 DBMS_OUTPUT.put_line ('Opened cursor');
11
12 FETCH l_cur INTO l_col;
13
14 DBMS_OUTPUT.put_line ('Fetched from cursor');
15
16 IF l_cur%NOTFOUND
17 THEN
18 DBMS_OUTPUT.put_line ('Oops! No data found. Raising exception...');
19 RAISE NO_DATA_FOUND;
20 END IF;
21
22 CLOSE l_cur;
23 EXCEPTION
24 WHEN NO_DATA_FOUND
25 THEN
26 DBMS_OUTPUT.put_line ('Exception raised.');
27 END;
28 /
Opened cursor
Fetched from cursor
Oops! No data found. Raising exception...
Exception raised.
PL/SQL procedure successfully completed.
To slightly modify #Eddie Awad's answer, the usual code pattern I use for fetching from a cursor variable is as follows:
DECLARE
l_cur SYS_REFCURSOR;
l_col VARCHAR2 (10);
BEGIN
OPEN l_cur FOR
SELECT 'Hi there' col
FROM DUAL
WHERE 1 = 0;
DBMS_OUTPUT.PUT_LINE('Opened cursor');
<<cursor_loop>>
LOOP
FETCH l_cur INTO l_col;
DBMS_OUTPUT.PUT_LINE('Fetched from cursor');
EXIT cursor_loop WHEN l_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Process data fetched from cursor');
END LOOP; -- cursor_loop
CLOSE l_cur;
DBMS_OUTPUT.PUT_LINE('Closed cursor');
END;
The idea is to open the cursor variable (or get it back from a procedure), then loop until all rows have been fetched from the cursor.
Share and enjoy.