while doing the testing in migrating oracle to PostgreSQL i am facing issue in code:-
create or replace PROCEDURE clear_delete_tables
/*
clear_delete_tables
Truncate all the del_ tables
*/
IS
v_code NUMBER;
v_errm VARCHAR2(64);
CURSOR cur_del_tables IS
SELECT table_name
FROM user_tables
WHERE table_name LIKE 'DEL!_%' ESCAPE '!';
BEGIN
For i in cur_del_tables
LOOP
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || i.table_name;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
v_code := SQLCODE;
v_errm := SUBSTR(SQLERRM, 1 , 64);
log_entry($$PLSQL_UNIT, v_code || ' - ' || v_errm);
END clear_delete_tables;
What is the replacement in $$PLSQL_UNIT in PostgreSQL? How I can change the entire code to compatible in PostgreSQL?
Related
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've number of tables that i want to drop some columns and add some another columns again. (oracle database)
All of tables are empty.
does it work??
DECLARE
CURSOR cursor_name
IS
SELECT TABLE_NAME
FROM SYS.ALL_TABLES
WHERE OWNER = 'username';
TN NVARCHAR2 (30);
TABLE_COUNT NUMBER (3);
TCDROP NVARCHAR2 (1000);
TCADD NVARCHAR2 (1000);
BEGIN
SELECT COUNT (1)
INTO TABLE_COUNT
FROM SYS.ALL_TABLES
WHERE OWNER = 'username';
OPEN cursor_name;
FOR i IN 1 .. TABLE_COUNT
LOOP
FETCH cursor_name INTO TN;
TCDROP := 'ALTER TABLE ' || TN || ' DROP (*columns list*);';
EXECUTE IMMEDIATE TCDROP;
TCADD :=
'ALTER TABLE ' || TN || ' ADD (*columns and datatype list*);';
EXECUTE IMMEDIATE TCADD;
EXIT WHEN cursor_name%NOTFOUND;
END LOOP;
CLOSE cursor_name;
END;
/
Yes, this will certainly work. I'd recommend to put in the owner/schema of the tables, though. Besides, it is unusual or unsafe to query the number of tables and the loop through the list. I'd put it in a simple for loop:
DECLARE
stmt VARCHAR2(1000);
BEGIN
FOR t IN (SELECT table_name FROM all_tables WHERE owner='XYZ') LOOP
stmt := 'ALTER TABLE '||owner||'.'||table_name||' DROP (*columns list*);';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE (stmt);
stmt:= 'ALTER TABLE '||owner||'.'||table_name||' ADD (*columns and datatype list*);';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE (stmt);
END LOOP;
END;
/
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;
I have a requirement to run a SP across multiple DBs at the same time and one part of it is to eliminate some duplicate records from each DB. Now since the SP can be run multiple times i've included a backup table and and what's needed to truncate and drop it in case the SP is run twice in a row.
Now, since i'm creating tables via DBLINK i've researched that i need to use dbms_utility.exec_ddl_statement - but in this case even though the procedure executes, the truncate and drop queries seem to do nothing, because when i run the SP the second time it fails telling me the name of the backup table is already in use (even though i've included the DROP execution before CREATE).
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'BEGIN'
||' EXECUTE IMMEDIATE ''truncate table iSecurity2_dupes_bak'';'
||' EXCEPTION'
||' WHEN OTHERS THEN'
||' IF SQLCODE != -942 THEN'
||' RAISE;'
||' END IF;'
||' END;';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
sql_update := 'BEGIN'
||' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';'
||' EXCEPTION'
||' WHEN OTHERS THEN'
||' IF SQLCODE != -942 THEN'
||' RAISE;'
||' END IF;'
||' END;';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
.................
ORA-00955: name is already used by an existing object
ORA-06512: at "SYS.DBMS_UTILITY", line 478
ORA-06512: at line 1
ORA-06512: at "database.procedure_name", line 53
ORA-06512: at line 2
The rest of the cursor which includes deletes, inserts, updates and creation of GLOBAL TEMP tables seems to work just fine and everything executes. If i manually delete the backup table even the CREATE that fails executes.
I'm perplexed :(
UPDATE 08/12/2016
With the help provided by #Jon Heller I was able to transform my code in the below which works as long as i use a static name for the DB_LINK. But when i try to use the variable it fails.
Tried both of the below versions but stil lcan't get to run however i keep modifying them - am i missing something here?
Note: Now, i added the alter session because without it, re-running the original procedure kept failing due to ORA-04062: timestamp of procedure "cw_drop_table" has been changed;
1st version
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
begin
dbms_utility.exec_ddl_statement#v_database_name (
q'[
create or replace procedure cw_drop_table is sql_drop varchar2(2000);
begin
sql_drop := 'BEGIN'
||' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';'
||' EXCEPTION'
||' WHEN OTHERS THEN IF SQLCODE != -942 THEN NULL; END IF; END;';
execute immediate sql_drop;
commit;
end; ]' );
execute immediate 'begin cw_drop_table#'||v_database_name||'; end;';
end;
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
PLS-00352: Unable to access another database 'V_DATABASE_NAME'
PLS-00201: identifier 'DBMS_UTILITY#V_DATABASE_NAME' must be declared
PL/SQL: Statement ignored
2nd version
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
declare v_db_name varchar2(100);
begin select v_database_name into v_db_name from dual;
execute immediate 'dbms_utility.exec_ddl_statement#'||v_db_name||' ('
||' q''[ '
||' create or replace procedure cw_drop_table is sql_drop varchar2(2000);'
||' begin '
||' sql_drop := ''BEGIN'' '
||' ||'' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';'' '
||' ||'' EXCEPTION'' '
||' ||'' WHEN OTHERS THEN IF SQLCODE != -942 THEN NULL; END IF; END;''; '
||' execute immediate sql_drop;'
||' commit;'
||' end; ]'' ); '
||' execute immediate ''begin cw_drop_table#'||v_db_name||'; end;''; ';
end;
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
PLS-00103: Encountered the symbol "DROP" when expecting one of the following:
* & = - + ; < / > at in is mod remainder not rem
<an exponent (**)> <> or != or ~= >= <= <> and or like LIKE2_
LIKE4_ LIKEC_ between || member SUBMULTISET_
SOLUTION
After much contemplation and a shower i abandoned the above methodology and went with the below. Not sure why i didn't think about it earlier :|
Note: if anyone ever reads through this long-winded question and knows what i did wrong in the 08/12/2016 Update, i am curious to find out :)
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
begin
sql_update:='DROP TABLE iSecurity2_dupes_bak';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
NULL; -- suppresses ORA-00942 exception
ELSE
RAISE;
END IF;
END;
DBMS_UTILITY.EXEC_DDL_STATEMENT only reliably runs DDL. If you try to run it with a PL/SQL block it will silently fail and not run anything.
This can be demonstrated by running a PL/SQL block that should obviously fail. The code below should generate ORA-01476: divisor is equal to zero. But instead it does nothing.
begin
dbms_utility.exec_ddl_statement#myself(
q'[declare v_test number; begin v_test := 1/0; end;]'
);
end;
/
Use a temporary procedure to run a PL/SQL block remotely. Create the procedure with DBMS_UTILITY.EXEC_DDL_STATEMENT and then call it with native dynamic SQL.
begin
dbms_utility.exec_ddl_statement#myself(
q'[
create or replace procedure test_procedure
is
v_test number;
begin
v_test := 1/0;
end;
]'
);
execute immediate 'begin test_procedure#myself; end;';
end;
/
RESULTS:
ORA-01476: divisor is equal to zero
ORA-06512: at "JHELLER.TEST_PROCEDURE", line 5
ORA-06512: at line 1
ORA-06512: at line 12
I think this behavior is a bug. Oracle should throw an error instead of simply not doing anything.
Welcome to concatenation hell. Strings get messy when they're embedded 4 levels deep. But there are a few things you can do to make life easier:
Use nested alternative-quoting mechanism. For example, q'[ ... ]', inside a q'< ... >', etc.
Use multi-line strings. There's no need to concatenate multiple lines, just use a single string.
Use extra spacing to help identify the start and end of strings. When things get this crazy it's worth putting a string delimiter on a line all by itself, so that everything is easy to line up.
Use REPLACE instead of concatenation.
I re-formatted part of your code using those tips. Stackoverflow does not understand the alternative quoting mechanism, but the strings should look better in a good Oracle SQL editor.
declare
v_db_name varchar2(30) := 'myself';
sql_update varchar2(32767);
begin
execute immediate replace(
q'[
begin
dbms_utility.exec_ddl_statement##DB_NAME#
(
q'<
create or replace procedure cw_drop_table is
sql_drop varchar2(2000);
begin
sql_drop :=
q'{
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE iSecurity2_dupes_bak';
EXCEPTION WHEN OTHERS THEN
IF SQLCODE != -942 THEN
NULL;
END IF;
END;
}';
execute immediate sql_drop;
end;
>'
);
execute immediate 'begin cw_drop_table##DB_NAME#; end;';
end;
]', '#DB_NAME#', v_db_name);
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_db_name||
'(:sql_update); end;' using sql_update;
commit;
end;
/
I want to iterate through all columns to find and replace a specific character. here is my pl/sql block:
Declare
match_count Number:=0;
v_search_string VARCHAR2(4000) := '%ي%';
BEGIN
FOR t IN
(SELECT owner,
table_name,
column_name
FROM all_tab_columns
WHERE (SUBSTR(table_name,1,2)='PN'
OR (SUBSTR(table_name,1,2) ='CD'
AND owner ='PNET_USER' ))
AND (data_type ='VARCHAR2'
OR data_type ='CLOB')
)
LOOP
BEGIN
EXECUTE IMMEDIATE 'SELECT count(*) FROM '||t.owner || '.' || t.table_name|| ' WHERE '||t.column_name||' LIKE :1' INTO match_count USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line( t.owner || '.' || t.table_name ||' '||t.column_name||' '||match_count );
--EXECUTE IMMEDIATE 'UPDATE '||t.table_name||' SET '||t.column_name||'=replace()'
END IF;
END;
END LOOP;
It works fine and prints the name of columns that have the invalid characters. But I don't know how to replace the characters. How can i get the value of t.column_name , replace the invalid character and then update the t.table_name ?
Well it was almost fine (and stevo's answer also). I let you change v_from and v_to with your chars.
Declare
match_count Number :=0;
v_from varchar2(5) := 'a';
v_like varchar2(5) := '%'||v_from||'%';
v_to varchar2(5) := 'b';
v_sql varchar2(1000);
v_emesg varchar2(1000);
CURSOR s is
(SELECT owner, table_name, column_name
FROM all_tab_columns
where SUBSTR(table_name,1,2) IN ('PN', 'CD')
AND owner ='PNET_USER'
AND data_type IN('VARCHAR2', 'CLOB'););
begin
for t in s LOOP
begin
EXECUTE IMMEDIATE 'SELECT count(*) FROM '||t.owner || '.' || t.table_name|| ' WHERE '||t.column_name||' LIKE :1' INTO match_count USING v_like;
IF match_count > 0 THEN
begin
dbms_output.put_line( t.owner || '.' || t.table_name ||' '||t.column_name||' '||match_count );
v_sql := 'UPDATE '||t.owner||'.'||t.table_name||' SET '||t.column_name||'= REPLACE('||t.column_name||', '''||v_from||''', '''||v_to||''') WHERE '||t.column_name||' LIKE '''|| v_like||'''';
dbms_output.put_line(v_sql);
EXECUTE IMMEDIATE v_sql;
EXCEPTION WHEN OTHERS THEN
v_emesg := SQLERRM;
dbms_output.put_line(v_emesg);
dbms_output.put_line('Errow while trying to update '||t.owner||'.'||.t.table_name||' : column '||t.column_name||'.';
END;
END IF;
end;
END LOOP;
end;
Something like
EXECUTE IMMEDIATE 'UPDATE '||t.table_name||' SET '||t.column_name||'=replace('||t.column_name||',''FROM'',''TO'')'
Should do it, where FROM is the invalid character and TO is whatever you want to change it to.
See oracle docs in link for an explanation of how REPLACE works.
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions134.htm.
NB I don't have anywhere to try this, so syntax might not be correct.