How can I pass column and table name as parameters to a SQL stored procedure - sql

CREATE OR REPLACE FUNCTION getIdFromNameParameter
(columnName VARCHAR2, tableName VARCHAR2, whereColumn VARCHAR2, parameterColumn VARCHAR2)
RETURN VARCHAR2
AS
idCatc VARCHAR2(50);
BEGIN
execute immediate 'SELECT ' || columnName || ' INTO ' || idCatc || ' FROM ' || tableName || ' WHERE ' || whereColumn || ' = ' || parameterColumn;
RETURN idCatc;
END;
/
I get this warning:
Warning: Function created with compilation errors

For one thing, the into is part of execute immediate:
CREATE OR REPLACE FUNCTION getIdFromNameParameter (
in_columnName VARCHAR2,
in_tableName VARCHAR2,
in_whereColumn VARCHAR2,
in_parameterColumn VARCHAR2)
RETURN VARCHAR2
AS
idCatc VARCHAR2(50);
BEGIN
execute immediate 'SELECT ' || in_columnName || ' FROM ' || in_tableName || ' WHERE ' || in_whereColumn || ' = ' || in_parameterColumn
INTO idCatc;
RETURN idCatc;
END;
You were also using + for string concatenation.

If you create a function like this, ensure it's safe from sql injection with something like the following. This uses dbms_assert to sanitize the inputs against thing like ';drop table xyz;'
CREATE OR REPLACE FUNCTION getidfromnameparameter (
in_columnname VARCHAR2,
in_tablename VARCHAR2,
in_wherecolumn VARCHAR2,
in_parametercolumn VARCHAR2
) RETURN VARCHAR2 AS
idcatc VARCHAR2(50);
BEGIN
EXECUTE IMMEDIATE 'SELECT '
|| sys.dbms_assert.qualified_sql_name(in_columnName)
|| ' FROM '
|| sys.dbms_assert.sql_object_name(in_tableName)
|| ' WHERE '
|| sys.dbms_assert.qualified_sql_name(in_whereColumn)
|| ' = '
|| sys.dbms_assert.enquote_literal(in_parametercolumn)
INTO idcatc;
RETURN idcatc;
END;
/

Related

Adding output to function PL/SQL

I am trying to get my function to include the date and current user, but I keep getting an error that my function is in an invalid state. If I comment out or remove the two dbms_output... lines it works just fine. Any ideas of how to return that output? Using Oracle SQL Developer
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
str2 VARCHAR2(10) := y;
BEGIN
RETURN str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;
You have to declare the result Variable first, also enable the dbms_output on your SQL Developer.
This should work.
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
str1 VARCHAR2(10) := x;
STR2 VARCHAR2(10) := Y;
result VARCHAR2(250);
BEGIN
result := str1 || str2;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || SYSDATE || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result;
END;
/
SELECT F_CONCATENATE_STRINGS('Crypto','Currency') FROM DUAL;
your function has some issues.
result is not declared
return statement should be the last statement in the function. it return a result and terminates the execution of the function
you should convert to char the sysdate
you can try this one:
CREATE OR REPLACE FUNCTION f_concatenate_strings(x VARCHAR2, y VARCHAR2)
RETURN VARCHAR2
AS
--str1 VARCHAR2(10) := x;
--str2 VARCHAR2(10) := y;
result VARCHAR2(20);
BEGIN
result := x || y;
dbms_output.put_line('The result is ' || result);
dbms_output.put_line('Date: ' || to_char(SYSDATE,'dd.mm.yyyy') || ' user: ' ||
SYS_CONTEXT('USERENV','OS_USER'));
RETURN result; -- move to the end of the function
END;
/
SELECT f_concatenate_strings('Crypto','Currency') FROM DUAL;

error when executing a varchar2 in a procedure

I have a varchar2 with an INSERT and I want to execute it in a procedure I try to do it with an execute but this happens:
EXECUTE IMMEDIATE sql_str;
Error:
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at "SYS.INSERT_MOVIMIENTOS", line 47
ORA-06512: at line 1
Varchar2 carries an insert that is this and works if I paste it but when executing it in the procedure something of the procedure fails.
INSERT INTO MOVIMIENTOS (COD_BANCO, COD_SUCUR, NUM_CTA, FECHA_MOV, TIPO_MOV, IMPORTE) VALUES (2000, 2000, 0, '11/11/08', 'I', 500);
My procedure
CREATE OR REPLACE PROCEDURE INSERT_MOVIMIENTOS (
INSERTMOV_COD_BANCO IN NUMBER,
INSERTMOV_COD_SUCUR IN NUMBER,
INSERTMOV_NUM_CTA IN NUMBER,
INSERTMOV_FECHA_MOV IN DATE,
INSERTMOV_TIPO_MOV IN CHAR,
INSERTMOV_IMPORTE IN NUMBER
)
IS
sql_str VARCHAR2(500) := 'INSERT INTO MOVIMIENTOS (';
movimiento movimientos_typ;
BEGIN
movimiento := movimientos_typ(
INSERTMOV_COD_BANCO,
INSERTMOV_COD_SUCUR,
INSERTMOV_NUM_CTA,
INSERTMOV_FECHA_MOV,
INSERTMOV_TIPO_MOV,
INSERTMOV_IMPORTE
);
IF movimiento.getCOD_BANCO() != 0 THEN
sql_str := sql_str || 'COD_BANCO, COD_SUCUR, NUM_CTA, FECHA_MOV, TIPO_MOV, IMPORTE) VALUES (' ||
movimiento.getCOD_BANCO() || ', ' ||
movimiento.getCOD_SUCUR() || ', ' ||
movimiento.getNUM_CTA() || ', ''' ||
movimiento.getFECHA_MOV() || ''', ''' ||
movimiento.getTIPO_MOV() || ''', ' ||
movimiento.getIMPORTE() || ');';
ELSE
sql_str := sql_str || 'COD_SUCUR, NUM_CTA, FECHA_MOV, TIPO_MOV, IMPORTE) VALUES (' ||
movimiento.getCOD_SUCUR() || ', ' ||
movimiento.getNUM_CTA() || ', ''' ||
movimiento.getFECHA_MOV() || ''', ''' ||
movimiento.getTIPO_MOV() || ''', ' ||
movimiento.getIMPORTE() || ');';
END IF;
DBMS_OUTPUT.PUT_LINE('////////////////////////////////////////');
DBMS_OUTPUT.PUT_LINE('CONSULTA: ' || sql_str);
DBMS_OUTPUT.PUT_LINE('////////////////////////////////////////');
DBMS_OUTPUT.PUT_LINE('DATOS INTRODUCIDOS: ');
movimiento.display;
DBMS_OUTPUT.PUT_LINE('////////////////////////////////////////');
EXECUTE IMMEDIATE sql_str;
DBMS_OUTPUT.PUT_LINE('FUNCION REALIZADA CON EXITO');
END;
/
Donot end with the semicolon ; in your query string.
movimiento.getIMPORTE() || ')';
The error is just because of it.
by the way, you should be using bind variables. Dynamically constructing the values this way is vulnerable to SQL Injection.

Generate change scripts from SQL

I want to change database development from creating change scripts to automatically generating change scripts from the declarative defitinitions. How would I go about doing that for PL/SQL on an Oracle DB?
You can find a short introduction at section "Version Datamodel" here on stackoverflow. I've worked on this interesting area for many years, starting in 1994 on Oracle7 to allow automatic overnight going in production. Not all nights I got my sleep...
For Invantive Producer, we have a boot-package that runs on Oracle 11 and up and takes care of automatic upgrades, but it is over 8.000 lines. The boot package is called by PL/SQL when bootstrapping and later on takes it instructions from a repository with software definitions. The repository is described at Technical Reference Manual, also a Diagram is available and might give some inspiration.
I can give you a sample of such as procedure, please let me know what types of objects you would like to automatically generate DDL for, then I can add things when necessary.
procedure verify_index
( p_table_name varchar2
, p_index_name varchar2
, p_index_unique boolean default null
, p_column_list varchar2
)
;
and
procedure verify_index
( p_mode pls_integer
, p_table_name varchar2
, p_index_name varchar2
, p_index_unique boolean default null
, p_column_list varchar2
)
is
begin
verify_index
( get_table_name(p_mode, p_table_name)
, get_index_name(p_mode, p_index_name)
, case
when p_mode = g_history_mode
then false
else p_index_unique
end
, p_column_list
|| case
when p_mode = g_history_mode
then ',h_date_starts,h_date_ends'
else ''
end
)
;
end;
--
-- Verify existence of an index with the indicated column list.
--
procedure verify_index
( p_table_name varchar2
, p_index_name varchar2
, p_index_unique boolean default null
, p_column_list varchar2
)
is
l_index_exists_anywhere boolean;
l_index_exists_on_table boolean;
l_create_index boolean;
l_ist_column_list varchar2(4000);
l_soll_column_list varchar2(4000);
l_story varchar2(4000);
l_stmt varchar2(4000);
begin
l_story := 'Reason: ';
--
-- Determine whether to recreate the index.
-- In the process, drop the current index when not correct.
--
l_index_exists_anywhere := index_exists(p_index_name, null, null);
l_index_exists_on_table := index_exists(p_index_name, null, p_table_name);
if l_index_exists_on_table
then
l_ist_column_list := index_column_list(p_index_name, true);
l_soll_column_list := lower(p_column_list);
if identical_character_sequence(l_ist_column_list, l_soll_column_list)
then
-- Column lists are equal. Just check uniqueness.
if index_exists(p_index_name, p_index_unique, p_table_name)
then
null; -- Do nothing.
l_create_index := false;
l_story := l_story || ' index ' || p_index_name || ' already exists on the correct table with the correct column list ' || l_soll_column_list || '. Do nothing.';
else
drop_index_existing(p_index_name);
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' has the correct column list ' || l_soll_column_list || ' but on a different table. Recreate index.';
end if;
else
drop_index_existing(p_index_name);
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' has the incorrect column list ' || l_ist_column_list || ' but should have ' || l_soll_column_list || '. Recreate index.';
end if;
elsif l_index_exists_anywhere
then
drop_index_existing(p_index_name);
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' exists on the wrong table. Recreate index.';
else
l_ist_column_list := null;
l_create_index := true;
l_story := l_story || ' index ' || p_index_name || ' does not yet exist. Create index.';
end if;
--
-- Now l_create_index describes whether to create the index or not.
--
if l_create_index
then
/* Only uncomment this when itgen_log is available. During bootstrapping it is not possible
to use itgen_log. */
-- itgen_log.debug('Create index ' || p_index_name || '. ' || l_story);
l_stmt := 'create'
|| case
when p_index_unique
then ' unique'
else ''
end
|| ' index '
|| p_index_name
|| ' on '
|| p_table_name
|| '('
|| p_column_list
|| ')'
;
execute_dynamic_sql(l_stmt);
end if;
end
;

PLSQL : Dynamic table record holder

If I want to fetch records from a table (table name is dynamic from input), how to define the record holder or how to fetch the data from this defined table? p_table_name%rowtype will not complie because p_table_name is a parameter, not a table name.
PROCEDURE do_scan(p_table_name IN VARCHAR2
,p_min_num IN NUMBER
,p_time_range IN NUMBER
,p_problem_desc OUT
,p_result_code OUT)
IS
TYPE ObjCurTyp IS REF CURSOR;
v_obj_cursor ObjCurTyp;
v_obj_record ???????(p_table_name%rowtype)
BEGIN
v_stmt_str := 'Select * from :t where date_started > TRUNC(SYSDATE-3)';
OPEN v_obj_cursor FOR v_stmt_str USING p_table_name;
LOOP
FETCH v_obj_cursor INTO v_obj_record;
EXIT WHEN v_obj_cursor %NOTFOUND;
END LOOP;
END do_scan;
You can put that code as a dynamic PL/SQL block within an EXECUTE IMMEDIATE statement.
PROCEDURE do_scan
(
p_table_name IN VARCHAR2
p_min_num IN NUMBER
p_time_range IN NUMBER
p_problem_desc OUT
p_result_code OUT
)
IS
BEGIN
EXECUTE IMMEDIATE
'DECLARE ' ||
' TYPE ObjCurTyp IS REF CURSOR; ' ||
' v_obj_cursor ObjCurTyp; ' ||
' v_obj_record ' || p_table_name || '%rowtype; '||
'BEGIN ' ||
' v_stmt_str := ''Select * from :t where ' ||
' date_started > TRUNC(SYSDATE-3)''; ' ||
' OPEN v_obj_cursor ' ||
' FOR v_stmt_str USING ' ||
p_table_name || '; ' ||
' LOOP ' ||
' FETCH v_obj_cursor INTO v_obj_record; ' ||
' EXIT WHEN v_obj_cursor %NOTFOUND; ' ||
' END LOOP; ' ||
'END;';
END do_scan;
Regards,
Dariyoosh

Finding specific data in Oracle Tables

I needed to find a value for a column in my oracle database but i don't know which
table or column it's stored in
How can I search for a specific or like %% data as I do in
select * from SYS.dba_source
is there a table like that
Column Name ID Data Type Null? Comments
OWNER 1 VARCHAR2 (30 Byte) Y
NAME 2 VARCHAR2 (30 Byte) Y Name of the object
TYPE 3 VARCHAR2 (12 Byte) Y
Type of the object:
"TYPE", "TYPE BODY", "PROCEDURE", "FUNCTION",
"PACKAGE", "PACKAGE BODY" or "JAVA SOURCE"
LINE 4 NUMBER Y Line number of this line of source
TEXT 5 VARCHAR2 (4000 Byte) Y Source text
LINK: pl/sq to find any data in a schema
Imagine, there are a few tables in your schema and you want to find a specific value in all columns within these tables. Ideally, there would be an sql function like
select * from * where any(column) = 'value';
Unfortunately, there is no such function.
However, a PL/SQL function can be written that does that. The following function iterates over all character columns in all tables of the current schema and tries to find val in them.
create or replace function find_in_schema(val varchar2)
return varchar2 is
v_old_table user_tab_columns.table_name%type;
v_where Varchar2(4000);
v_first_col boolean := true;
type rc is ref cursor;
c rc;
v_rowid varchar2(20);
begin
for r in (
select
t.*
from
user_tab_cols t, user_all_tables a
where t.table_name = a.table_name
and t.data_type like '%CHAR%'
order by t.table_name) loop
if v_old_table is null then
v_old_table := r.table_name;
end if;
if v_old_table <> r.table_name then
v_first_col := true;
-- dbms_output.put_line('searching ' || v_old_table);
open c for 'select rowid from "' || v_old_table || '" ' || v_where;
fetch c into v_rowid;
loop
exit when c%notfound;
dbms_output.put_line(' rowid: ' || v_rowid || ' in ' || v_old_table);
fetch c into v_rowid;
end loop;
v_old_table := r.table_name;
end if;
if v_first_col then
v_where := ' where ' || r.column_name || ' like ''%' || val || '%''';
v_first_col := false;
else
v_where := v_where || ' or ' || r.column_name || ' like ''%' || val || '%''';
end if;
end loop;
return 'Success';
end;